Timezone Generic Clock
/**************************************************************
TZ_NTP_Clock_WiFi.ino
For AVR, ESP8266/ESP32, SAMD21/SAMD51, nRF52, STM32, WT32_ETH01 boards
Based on and modified from Arduino Timezone Library (https://github.com/JChristensen/Timezone)
to support other boards such as ESP8266/ESP32, SAMD21, SAMD51, Adafruit's nRF52 boards, etc.
Copyright (C) 2018 by Jack Christensen and licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
Built by Khoi Hoang https://github.com/khoih-prog/Timezone_Generic
Licensed under MIT license
***************************************************************************/
#include "defines.h"
// included only in main(), .ino with setup() to avoid `Multiple Definitions` Error
#include // https://github.com/khoih-prog/Timezone_Generic
//************************* DEFINES ************************************
#define TIMEZONE_GENERIC_VERSION_MIN_TARGET "Timezone_Generic v1.10.1"
#define TIMEZONE_GENERIC_VERSION_MIN 1010001
#define USING_INITIALIZED_TZ false //true
//************************* PROTOTYPES ************************************
//************************* VARIABLES ************************************
#if USING_INITIALIZED_TZ
// US Eastern Time Zone (New York, Detroit,Toronto)
TimeChangeRule myDST = {"EDT", Second, Sun, Mar, 2, -240}; // Daylight time = UTC - 4 hours
TimeChangeRule mySTD = {"EST", First, Sun, Nov, 2, -300}; // Standard time = UTC - 5 hours
Timezone *myTZ;
#else
// Allow a "blank" TZ object then use begin() method to set actual TZ.
// Feature added by 6v6gt (https://forum.arduino.cc/index.php?topic=711259)
Timezone *myTZ;
TimeChangeRule myDST;
TimeChangeRule mySTD;
#endif
// If TimeChangeRules are already stored in EEPROM, comment out three
// lines above and uncomment line below.
//Timezone myTZ( 100 ); // assumes rules stored at EEPROM address 100
TimeChangeRule *tcr; // pointer to time change rule, use to get TZ abbrev
int status = WL_IDLE_STATUS; // Wifi radio's status
char timeServer[] = "time.nist.gov"; // NTP server
unsigned int localPort = 2390; // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48; // NTP timestamp is in first 48 bytes of message
const int UDP_TIMEOUT = 2000; // timeout in miliseconds to wait for an UDP packet to arrive
byte PktBuff[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp;
/*F********************************************************************
*
**********************************************************************/
void
setup()
{
Serial.begin( BAUD );
while( !Serial && millis() < 5000);
delay( 200 );
Serial.print( F("\nStart TZ_NTP_Clock_WiFi on "));
Serial.println( BOARD_NAME );
Serial.println( TIMEZONE_GENERIC_VERSION );
#if defined( TIMEZONE_GENERIC_VERSION_MIN )
if( TIMEZONE_GENERIC_VERSION_INT < TIMEZONE_GENERIC_VERSION_MIN)
{
Serial.print( "Warning. Must use this example on Version equal "
"or later than : ");
Serial.println( TIMEZONE_GENERIC_VERSION_MIN_TARGET);
}
#endif
if( WiFi.status() == WL_NO_SHIELD) // CHECK FOR PRESENCE OF SHIELD
{
Serial.println( F( "WiFi shield not present"));
while( true ); // DON'T CONTINUE
}
while( status != WL_CONNECTED) // ATTEMPT TO CONNECT TO WiFi NETWORK
{
Serial.print( F("Connecting to WPA SSID: "));
Serial.println( ssid);
status = WiFi.begin( ssid, pass ); // CONNECT TO WPA/WPA2 NETWORK
}
// YOU'RE CONNECTED NOW, SO PRINT OUT DATA
Serial.print( F( "You're connected to network, IP = "));
Serial.println( WiFi.localIP() );
#if( USING_INITIALIZED_TZ )
myTZ = new Timezone( myDST, mySTD );
#else
String tzName = "EDT/EST" ; // CAN READ THIS INFO FROM EEPROM, STORAGE, ETC
// TIME ZONE RULES CAN BE SET AS BELOW OR DYNAMICALLY BUILT, SAY THROUGH A CONFIGURATION
if( tzName == "EDT/EST" ) // INTERFACE, OR FETCHED FROM EEPROM, FLASH ETC
{
// AMERICA EASTERN TIME
myDST = (TimeChangeRule) {"EDT", Second, Sun, Mar, 2, -240};// DST time = UTC - 4 hours
mySTD = (TimeChangeRule) {"EST", First, Sun, Nov, 2, -300};// STD = UTC - 5 hours
}
else if( tzName == "CET/CEST" )
{
// central Europe
myDST = (TimeChangeRule) {"CEST", Last, Sun, Mar, 2, 120};
mySTD = (TimeChangeRule) {"CET", Last, Sun, Oct, 3, 60};
}
else if( tzName == "GMT/BST" )
{ // UK
myDST = (TimeChangeRule) {"BST", Last, Sun, Mar, 1, 60};
mySTD = (TimeChangeRule) {"GMT", Last, Sun, Oct, 2, 0};
}
myTZ = new Timezone();
myTZ->init( myDST, mySTD );
#endif
myTZ->writeRules();
Udp.begin(localPort);
Serial.print(F("Listening on port "));
Serial.println(localPort);
}
/*F********************************************************************
*
**********************************************************************/
void
loop()
{
getNTPTime();
displayClock();
}
/*F********************************************************************
* send an NTP request to time server at given address
**********************************************************************/
void
sendNTPpacket( char *ntpSrv)
{
// set all bytes in buffer to 0
memset( PktBuff, 0, NTP_PACKET_SIZE );
// Initialize values needed to form NTP request
// (see URL above for details on packets)
PktBuff[0] = 0b11100011; // LI, Version, Mode
PktBuff[1] = 0; // Stratum, or type of clock
PktBuff[2] = 6; // Polling Interval
PktBuff[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
PktBuff[12] = 49;
PktBuff[13] = 0x4E;
PktBuff[14] = 49;
PktBuff[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(ntpSrv, 123); //NTP requests are to port 123
Udp.write(PktBuff, NTP_PACKET_SIZE);
Udp.endPacket();
}
/*F********************************************************************
* format and print a time_t value, with a time zone appended.
**********************************************************************/
void
printDateTime( time_t t, const char *tz)
{
char buf[32];
char m[4]; // temp month string stg (DateStrings.cpp uses shared buffer)
strcpy( m, monthShortStr(month(t)));
sprintf( buf, "%.2d:%.2d:%.2d %s %.2d %s %d %s", hour(t), minute(t)
, second(t), dayShortStr(weekday(t)), day(t), m, year(t), tz);
Serial.println( buf );
}
/*F********************************************************************
*
**********************************************************************/
void
displayClock( void )
{
time_t utc = now();
time_t local = myTZ->toLocal(utc, &tcr);
Serial.println();
printDateTime(utc, "UTC");
printDateTime(local, tcr -> abbrev);
delay(10000);
}
/*F********************************************************************
*
**********************************************************************/
void
getNTPTime( void )
{
static bool gotCurrentTime = false;
if( !gotCurrentTime ) // JUST GET CORRECT TIME ONCE
{
sendNTPpacket( timeServer ); // SEND AN NTP REQUEST TO A TIME SERVER
delay( 1000 ); // WAIT TO SEE IF A REPLY IS AVAILABLE
if( Udp.parsePacket())
{
Serial.println(F("Packet received"));
// RECEIVED A PACKET, READ DATA FROM IT
Udp.read( PktBuff, NTP_PACKET_SIZE);
// TIMESTAMP STARTS AT BYTE 40 OF RECEIVED PACKET AND IS FOUR BYTES,
// OR TWO WORDS, LONG. FIRST, EXTRACT TWO WORDS:
unsigned long highWord = word( PktBuff[40], PktBuff[41]);
unsigned long lowWord = word( PktBuff[42], PktBuff[43]);
// COMBINE FOUR BYTES (TWO WORDS) INTO A LONG INT
// THIS IS NTP TIME (SECONDS SINCE JAN 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print( F( "Seconds since Jan 1 1900 = "));
Serial.println(secsSince1900);
// NOW CONVERT NTP TIME INTO EVERYDAY TIME
Serial.print( F( "Unix time = "));
// UNIX TIME STARTS ON jAN 1 1970. iN SECONDS, THAT'S 2208988800:
const unsigned long seventyYears = 2208988800UL;
// SUBTRACT SEVENTY YEARS:
unsigned long epoch = secsSince1900 - seventyYears;
time_t epoch_t = epoch; // GET EPOCH TIME_T
// SET SYSTEM TIME TO UTC
// WARNING: ASSUMES THAT COMPILEtIME() RETURNS US EDT
// ADJUST FOLLOWING LINE ACCORDINGLY IF YOU'RE IN ANOTHER TIME ZONE
setTime( epoch_t );
// PRINT Unix TIME:
Serial.println( epoch );
// PRINT HOUR, MINUTE AND SECOND:
Serial.print( F( "The UTC time is ")); // UTC TIME
Serial.print( (epoch % 86400L) / 3600); // PRINT HOUR (86400 == SECS / DAY)
Serial.print( ':' );
if( ((epoch % 3600) / 60) < 10)
Serial.print( '0' ); // 1st 10 MIN OF HOUR, NEED LEADING '0'
Serial.print( (epoch % 3600) / 60); // PRINT MINUTE (3600 == SECS / MIN)
Serial.print(':');
if( (epoch % 60) < 10)
Serial.print( '0' ); // IN 1ST 10 SECS OF EACH MIN NEED LDNG '0'
Serial.println( epoch % 60 ); // PRINT SECOND
gotCurrentTime = true;
}
else
delay( 10000 ); // WAIT TEN SECONDS BEFORE ASKING FOR TIME AGAIN
}
}